home *** CD-ROM | disk | FTP | other *** search
-
- Unit bTree; { Zak's Binary Tree Object / routines.. }
-
- {$O+,F+} { allow overlays }
-
- Interface
- Type KeyType = String[35]; {This can be changed if needed .., int, word, etc}
-
- Type StatusType = (Used,Free);
- Type ShowAllFuncType = Function (k:keytype;var Data):boolean;
-
- LeafType = record { A "living" leaf }
- Status: StatusType; { Status of node .. unused but useful }
- Mother,Left,Right:longint; { pointers to Parent, Left, and Right nodes }
- Key: KeyType; { the keyed data }
- end;
- GenericProcedure = procedure; { used to dispay balancing status }
-
- FileHeaderType = record
- DataRecSize, { size of data records }
- Root, { pointer to root node }
- NextFree: longint; { next free, unused node }
- end;
-
- DirectionType = (Right,Left); { the directions, duh }
-
- DeletedLeaf = record { a "dead" leaf -- overlaps old LeafType }
- Status : StatusType; { node status, hopefully Free}
- NextFree: longint; { pointer to next unused, free node }
- Filler : array[1..2]
- of longint; { pad LeafType.Left and Right }
- Filler2 : KeyType; { pad LeafType.Key }
- end;
-
- pbTreeObj = ^bTreeObj;
- bTreeObj = Object
-
- Constructor Init ( filename:string ; DataRecSize_:longint );
-
- { Initialize the object.. DataRecSize_ is ignored if the file is not
- new (has been Init'd before)}
-
- Destructor Done;
-
- { unused the memory and close the file }
-
- Function Add (Key: keytype; Var Data):boolean;
-
- { Add Data by Key -- returns FALSE if key exists, otherwise TRUE }
-
- Function Find (key: keytype):boolean;
-
- { returns TRUE if key could be found, FALSE otherwise }
-
- Function FindData (key: keytype; var data):boolean;
-
- { if key is found, then returns TRUE and correct data, FALSE otherwise }
-
- Function Delete (key: keytype):boolean;
-
- { returns TRUE if successful, FALSE if key not found }
-
- Function BalanceHeapReq:longint;
-
- { returns bytes of heap required for a Balance }
-
- Procedure Balance (Reading,Sorting,Updating:GenericProcedure);
-
- { Makes the AVERAGE number of links needed to find a key the least
- possible }
-
- Procedure ShowAll (func:ShowAllFuncType);
-
- { cycles through all the nodes, calling func until it returns FALSE
- or no more nodes.. }
-
- function Update(key:keytype; Var Data):boolean;
-
- { if key found, writes new Data to it, otherwise returns FALSE }
-
- private { INTERNAL to the object }
- f:file; { the file we're playing with }
- dataRecSize:longint; { current data record size }
- Function RecOfs (n:longint):longint;
- { returns offset of given record }
- Procedure ReadRecLeaf (n:longint;var RecHdr:LeafType);
- { reads only the LeafType of record n }
- Procedure ReadRecBoth (n:longint;var RecHdr:LeafType;var data);
- { reads both the LeafType and the data }
- Procedure WriteRecLeaf (n:longint;RecHdr:LeafType);
- { writes only the LeafType}
- Procedure WriteRecBoth (n:longint;RecHdr:LeafType;var data);
- { write both the LeafType and Data }
- Procedure WriteRecData (n:longint;var data);
- { just write the data for record n }
- Function NumRecords (filehdr:fileheadertype):longint;
- { returns number of total records in file }
- Function GetNewRecNum (filehdr:fileheadertype):longint;
- { returns next free record number }
- Procedure ReadFileHdr (var filehdr:fileheadertype);
- { reads the file header .. cryptic, eh? }
- Procedure WriteFileHdr (filehdr:fileheadertype);
- { writes the file's header }
- Procedure FindNewMother(r:longint;filehdr:fileheadertype);
- { reassign this node a new, more suitable, parent when orphaned :-) }
- Function FindKeyRec (key: keytype):longint;
- { returns record number with this key, 0 otherwise }
- end;
-
- Implementation
- uses Dos;
-
- Constructor bTreeObj.Init( filename:string; datarecsize_:longint );
- var fileheader:fileheadertype;
- t:word;
- begin
- {$I-}
- assign(f,filename);
- reset(f,1);
- {$I+}
- t:=ioresult;
- Case t of
- 0: begin { file exists.. ok so far }
- ReadFileHdr(fileheader);
- datarecsize:=fileheader.datarecsize; { init. prv. datarecsize }
- end;
- 2: begin { new file, let's initialize it, ok? }
- ReWrite(f,1);
- FileHeader.DataRecSize:=DataRecSize_; { setup header data }
- datarecsize:=datarecsize_;
- FileHeader.Root:=0;
- FileHeader.NextFree:=0;
- BlockWrite(f,FileHeader,Sizeof(FileHeader)) { write header data }
- end
- else RunError(t); { some other error .. }
- end
- end;
-
- Procedure bTreeObj.ShowAll (func:ShowAllFuncType);
- var fileheader:fileheadertype;
- rh :leaftype;
- data :pointer;
- cont :boolean;
- procedure climb(r:longint);
- var right:longint;
- begin
- ReadRecboth(r,rh,Data^);
- right:=rh.right;
- if not(rh.left=0) then
- begin
- Climb(rh.left);
- ReadRecBoth(r,rh,data^) { read back current data if needed }
- end;
- if not cont then exit; { "just checking" }
- cont := func(rh.key,data^);
- if not cont then exit;
- if not(right=0) then Climb(right);
- end;
- begin
- cont := true;
- ReadFileHdr(fileheader);
- GetMem(data,fileheader.datarecsize);
- if fileheader.root<>0 then Climb(fileheader.root);
- FreeMem(data,fileheader.datarecsize);
- end;
-
-
- Destructor bTreeObj.Done;
- begin
- close(f) { just close the file.. no big deal }
- end;
-
- Function bTreeObj.Add(Key: keytype; var data):boolean;
- var FileHdr: FileHeaderType;
- RecHdr : LeafType;
- Procedure AddNewRec;
- Function FindMother(var direction:directiontype):longint;
- var RecHdr :leaftype;
- LastNode:longint;
- procedure Search_Tree(n:longint);
- begin
- ReadRecLeaf(n,RecHdr);
- if Key>RecHdr.Key then
- if not(RecHdr.Right=0) then Search_Tree(RecHdr.Right) else
- begin
- LastNode:=n;
- Direction:=Right;
- end
- else if Key<RecHdr.Key then
- if not(RecHdr.Left=0) then Search_Tree(RecHdr.Left) else
- begin
- LastNode:=n;
- Direction:=left;
- end;
- end;
- begin
- Search_Tree(filehdr.root);
- FindMother:=LastNode;
- end;
- var MotherRec :longint;
- MotherRecHdr :Leaftype;
- MotherDirection:directiontype;
- NewRecNum :longint;
- NewRecHdr :leaftype;
- begin
- MotherRec:=FindMother(MotherDirection); { find available mother node }
- ReadRecLeaf(MotherRec,MotherRecHdr); { "read her data" }
- NewRecNum := GetNewRecNum(filehdr); { get next free record number }
- if not(NewRecNum>NumRecords(filehdr)) then
- begin
- ReadRecLeaf(NewRecNum,NewRecHdr);
- FileHdr.NextFree:=DeletedLeaf(NewRecHdr).NextFree;
- end;
- Case MotherDirection of
- Right: MotherRecHdr.Right:=NewRecNum;
- Left : MotherRecHdr.Left :=NewRecNum;
- end;
- With NewRecHdr do { initialize record.. }
- begin
- Status := used;
- Right := 0;
- Left := 0;
- Mother := MotherRec;
- end;
- NewRecHdr.Key:=Key;
- WriteFileHdr(FileHdr); { update file header }
- WriteRecLeaf(MotherRec,MotherRecHdr); { write mother }
- WriteRecBoth(newrecnum,NewRecHdr,Data); { write daughter }
- end;
- procedure AddFirstRec;
- begin { we're adding the first record in the file.. scary eh? }
- With RecHdr do { init. it }
- begin
- Status := Used;
- Right := 0;
- Left := 0;
- Mother := 0;
- end;
- RecHdr.key:=key;
- FileHdr.Root := 1;
- FileHdr.NextFree := 0;
- Seek(f,0);
- BlockWrite(f,Filehdr,sizeof(filehdr));
- BlockWrite(f,RecHdr,Sizeof(RecHdr));
- BlockWrite(f,data,filehdr.datarecsize);
- end;
- begin
- if not Find(key) then { if not found, then .. }
- begin
- ReadFileHdr(filehdr);
- if FileHdr.Root=0 then
- AddFirstRec
- else
- AddNewRec;
- add := true;
- end
- else Add := false;
- end;
-
- Function bTreeObj.Find (key: keytype):boolean;
- begin
- Find:=FindKeyRec(key)>0; { or BOOLEAN(FindKey(key)) would work too }
- end;
-
- Function bTreeObj.Update(key:keytype; Var Data):boolean;
- var i:longint;
- begin
- i:=FindKeyRec(key);
- if i=0 then
- begin
- Update:=False;
- end
- else
- begin
- WriteRecData(i,data);
- update:=true;
- end
- end;
-
- Function bTreeObj.FindData (key: keytype; var data):boolean;
- var filehdr:fileheadertype;
- rechdr :leaftype;
- r :longint;
- begin
- r:=FindKeyRec(key);
- if r>0 then
- begin
- ReadRecBoth(r,rechdr,data);
- FindData:=true;
- end
- else
- finddata:=false
- end;
-
- Function bTreeObj.Delete(key: keytype):boolean;
- var filehdr:fileheadertype;
- procedure Unlink(r:longint;var delhdr:leaftype);
- Function GetDirection(sonhdr:leaftype):directiontype;
- var sonrighthdr,sonlefthdr,motherhdr:leaftype;
- sre,sle:boolean;
- begin
- ReadRecLeaf(sonhdr.mother,motherhdr);
- if not(motherhdr.left=0) then
- begin
- ReadRecLeaf(motherhdr.left,sonlefthdr);
- sle:=true
- end
- else sle:=false;
- if not(motherhdr.right=0) then
- begin
- ReadRecLeaf(motherhdr.right,sonrighthdr);
- sre:=true;
- end
- else sre:=false;
- {$B-}
- if sle and not sre then GetDirection:=Left
- else if sre and not sle then GetDirection:=Right
- else if (sle and sre) and (sonrighthdr.key=sonhdr.key) then GetDirection:=Right
- else if (sle and sre) and (sonlefthdr.key=sonhdr.key) then GetDirection:=left;
- {$B+}
- end;
-
- var MotherHdr:leaftype;
- direction:directiontype;
- begin
- if not(DelHdr.Mother=0) then
- begin
- ReadRecLeaf(DelHdr.Mother,MotherHdr);
- Direction:=GetDirection(DelHdr);
- case Direction Of
- Left : MotherHdr.Left:=0;
- Right: MotherHdr.Right:=0;
- end;
- WriteRecLeaf(delhdr.mother,motherhdr);
- end
- end;
-
- Procedure UpdateFreeList(r:longint);
- function LastFree:longint;
- var rechdr:leaftype;n,ths:longint;
- begin
- n:=filehdr.nextfree;
- ths:=n;
- repeat
- begin
- ReadRecLeaf(n,rechdr);
- ths:=n;
- n:=deletedleaf(rechdr).nextfree;
- end
- until DeletedLeaf(RecHdr).nextfree=0;
- LastFree:=ths;
- end;
-
- Var updatedptrhdr:leaftype;lf:longint;
- begin
- if filehdr.nextfree=0 then
- begin
- filehdr.nextfree:=r;
- writefilehdr(filehdr);
- end
- else
- begin
- lf:=lastfree;
- ReadRecLeaf(Lf,updatedptrhdr);
- DeletedLeaf(updatedptrhdr).nextfree:=r;
- WriteRecLeaf(lf,updatedptrhdr);
- end;
- end;
-
- Procedure AddChildren(var dhdr:leaftype);
- begin
- if not(dhdr.left=0) then FindNewMother(dhdr.left,filehdr);
- if not(dhdr.right=0) then FindNewMother(dhdr.right,filehdr);
- end;
-
- Procedure ChangeMother(r,tor:longint);
- var rechdr:leaftype;
- begin
- ReadRecLeaf(r,rechdr);
- rechdr.mother:=tor;
- WriteRecLeaf(r,rechdr);
- end;
-
- { this is huge }
-
- var DelRecNum:longint;
- delhdr :leaftype;
- begin
- ReadFileHdr(filehdr);
- DelRecNum:=FindKeyRec(key); { find the record we're refering to }
- DelHdr.Status:=Free; { change its status }
- if not(DelRecNum>0) then Delete:=False else
- begin
- ReadRecLeaf(delrecnum,delhdr); { read the dead-to-be's header }
- if delhdr.Mother=0 then
- { we're dealing with the ROOT node ! }
- begin
- Delete:=true;
- UpdateFreeList(delrecnum); { add to free list }
- if not(delhdr.Right=0) then
- begin
- FileHdr.Root := delhdr.Right;
- ChangeMother(delhdr.Right,0);
- if not(delhdr.left=0) then FindNewMother(delhdr.left,filehdr);
- end;
- if not(delhdr.left=0) and (delhdr.right=0) then
- begin
- FileHdr.Root := delhdr.Left;
- ChangeMother(delhdr.Left,0);
- end;
- if (delhdr.right=0) and (delhdr.left=0) then
- begin
- FileHdr.Root:=0;
- end;
- DelHdr.Status:=Free;
- WriteFileHdr(filehdr);
- DeletedLeaf(DelHdr).NextFree:=0;
- WriteRecLeaf(delrecnum,delhdr);
- end
- else
- { the easy part }
- begin
- Delete:=true;
- Unlink(DelRecNum,delhdr); { unlink it from its parent }
- UpdateFreeList(delrecnum); { add to free list }
- DeletedLeaf(DelHdr).NextFree:=0; { this is the last in the chain .. }
- WriteRecLeaf(delrecnum,delhdr);
- AddChildren(delhdr); { re-classify its offspring }
- end;
- end;
- end;
-
- Function bTreeObj.BalanceHeapReq:longint;
- var rechdr :leaftype;
- filehdr :fileheadertype;
- numnodes :longint;
- procedure Climb(r:longint);
- begin
- ReadRecLeaf(r,rechdr);
- if not(rechdr.left=0) then Climb(rechdr.left);
- ReadRecLeaf(r,rechdr);
- inc(numnodes);
- if not(rechdr.right=0) then Climb(rechdr.right);
- end;
- begin
- numnodes:=0;
- readfilehdr(filehdr);
- if not(FileHdr.Root=0) then Climb(FileHdr.Root);
- balanceheapreq:=numnodes*20; { sizeof(ListRecType) }
- end;
-
- Procedure bTreeObj.Balance(Reading,Sorting,Updating:GenericProcedure );
- type ToListRecType = ^ListRecType;
- ListRecType = Record
- node,mother,left,right:longint;
- Next:ToListRecType;
- end;
- var filehdr : fileheadertype;
- ListRecRoot : ToListRecType;
- NumNodes : longint;
- MarkMem : pointer;
- Procedure ReadFileToLL;
- var rechdr :leaftype;
- curlistrec:tolistrectype;
- Procedure Add(r:longint);
- begin
- inc(NumNodes);
- if CurListRec=Nil then
- begin
- new(CurListRec);
- CurListRec^.Next := Nil;
- ListRecRoot := CurListRec;
- end
- else
- begin
- New(CurListRec^.next);
- CurListRec:=CurListRec^.Next;
- CurListRec^.Next := Nil;
- end;
- CurListRec^.Node:=r;
- CurListRec^.Mother:=0;
- CurListRec^.Left:=0;
- CurListRec^.Right:=0;
- end;
- procedure Climb(r:longint);
- begin
- ReadRecLeaf(r,rechdr);
- if not(rechdr.left=0) then Climb(rechdr.left);
- ReadRecLeaf(r,rechdr);
- Add(r);
- if not(rechdr.right=0) then Climb(rechdr.right);
- end;
- begin
- CurListRec:=ListRecRoot;
- if not(FileHdr.Root=0) then Climb(FileHdr.Root);
- end;
- Procedure GetRecNumInfo(n:longint; var mother,left,right:longint);
- var c:tolistrectype;
- begin
- c:=listrecroot;
- while c^.node<>n do c:=c^.next;
- mother:=c^.mother;
- left:=c^.left;
- right:=c^.right;
- end;
- Procedure PutRecNumInfo(n,mother,left,right:longint);
- var c:tolistrectype;
- begin
- c:=listrecroot;
- while c^.node<>n do c:=c^.next;
- c^.mother:=mother;
- c^.left:=left;
- c^.right:=right;
- end;
- Function Power(b,e:longint):longint;
- var t,c:longint;
- begin
- t:=b;
- if e=0 then begin power:=1 ; exit end;
- for c:=1 to e-1 do t:=t*b;
- power:=t;
- end;
- Procedure ProcessLL;
- var MaxNumNodes: longint;
- NumSubLevels : longint;
- TempMother,TempRight,TempLeft:longint;
- Modifier : longint;
- Function FindNumSubLevels(n:longint):longint;
- var i:longint;
- begin
- i:=1;
- repeat inc(i,1) until (power(2,i)>=n+1);
- FindNumSubLevels:=i-1;
- end;
- Function RightMod(root,modi:longint):longint;
- begin
- repeat
- begin
- modi := modi div 2;
- end
- until root+modi<=numnodes;
- RightMod := modi;
- end;
- Procedure FixSubTree(root:longint;mthr:longint);
- var sr:longint;
- begin
- if not(abs(mthr-root)=1) then
- begin
- modifier:=abs(mthr-root) div 2;
- templeft:=root-modifier;
- if (root+modifier<=NumNodes) then
- tempright:=root+modifier
- else
- begin
- modifier:=Rightmod(root,modifier);
- if not(modifier=0) then TempRight:=root+modifier else tempright:=0;
- end;
- tempmother:=mthr;
- PutRecNumInfo(root,tempmother,templeft,tempright);
- sr:=tempright;
- if not(templeft=0) then FixSubTree(templeft,root);
- if not(sr=0) then FixSubTree(sr,root);
- end
- else { lowest leaves }
- begin
- PutRecNumInfo(root,mthr,0,0);
- end;
- end;
- Function MaxNodes:longint;
- var i:longint;
- begin
- i:=0;
- repeat inc(i,1) until (power(2,i+1)-1)>=NumNodes;
- MaxNodes:= Power(2,i+1)-1;
- end;
- Var NewRoot:longint;
- begin
- MaxNumNodes := MaxNodes;
- NumSubLevels := FindNumSubLevels(MaxNumNodes); { number of "shelves" }
- if NumNodes<2 then NewRoot:=FileHdr.Root else NewRoot:=Power(2,NumSubLevels);
- FileHdr.Root := NewRoot;
- FixSubTree(NewRoot,0);
- end;
- Procedure WriteLLtoFile;
- var CurListRec: tolistrectype;
- l:leaftype;
- begin
- curlistrec:=listrecroot;
- while curlistrec<>nil do
- begin
- ReadRecLeaf(curlistrec^.node,l);
- l.left:=curlistrec^.left;
- l.right:=curlistrec^.right;
- l.mother:=curlistrec^.mother;
- WriteRecLeaf(curlistrec^.node,l);
- curlistrec:=curlistrec^.next;
- end;
- end;
- begin
- NumNodes := 0;
- ListRecRoot:=nil;
- Mark(MarkMem);
- ReadFileHdr(filehdr);
- reading; { status }
- if not(filehdr.root=0) then ReadFileToLL; { if there are >0 records then }
- sorting; { status } { read data into the linked list}
- if not(filehdr.root=0) then ProcessLL; { change data in LL }
- updating; { status }
- if not(filehdr.root=0) then WriteLLtoFile; { updated disk with LL data }
- WriteFileHdr(filehdr);
- Release(MarkMem);
- end;
-
- {privates}
-
- Function bTreeObj.RecOfs(n:longint):longint;
- begin
- RecOfs:=Sizeof(FileHeaderType)+((n-1)*(DataRecSize+Sizeof(LeafType)));
- end;
-
- Procedure bTreeObj.ReadRecLeaf(n:longint;var RecHdr:LeafType);
- begin
- seek(f,recofs(n));
- blockread(f,rechdr,sizeof(leaftype));
- end;
-
- Procedure bTreeObj.ReadRecBoth(n:longint;var RecHdr:LeafType;var data);
- begin
- seek(f,recofs(n));
- blockread(f,rechdr,sizeof(rechdr));
- blockread(f,data,datarecsize);
- end;
-
- Procedure bTreeObj.WriteRecLeaf(n:longint;RecHdr:LeafType);
- begin
- seek(f,recofs(n));
- blockwrite(f,rechdr,sizeof(rechdr));
- end;
-
- Procedure bTreeObj.WriteRecBoth(n:longint;RecHdr:LeafType;var data);
- begin
- seek(f,recofs(n));
- blockwrite(f,rechdr,sizeof(rechdr));
- blockwrite(f,data,datarecsize);
- end;
-
- Procedure bTreeObj.WriteRecData (n:longint;var data);
- begin
- Seek(f,recofs(n)+Sizeof(LeafType));
- blockwrite(f,data,datarecsize);
- end;
-
- Function bTreeObj.NumRecords(filehdr:fileheadertype):longint;
- var tv:longint;
- begin
- NumRecords := (FileSize(f)-Sizeof(FileHdr)) div (Sizeof(LeafType)+FileHdr.DataRecSize);
- end;
-
- Function bTreeObj.GetNewRecNum(filehdr:fileheadertype):longint;
- begin
- if filehdr.nextfree=0 then
- begin
- GetNewRecNum := NumRecords(filehdr)+1;
- exit
- end
- else
- GetNewRecNum := FileHdr.NextFree;
- end;
-
- Procedure bTreeObj.ReadFileHdr(var filehdr:fileheadertype);
- begin
- seek(f,0);
- blockread(f,FileHdr, sizeof(filehdr));
- end;
-
- Procedure bTreeObj.WriteFileHdr( filehdr:fileheadertype);
- begin
- seek(f,0);
- blockwrite(f,FileHdr, sizeof(filehdr));
- end;
-
- Procedure bTreeObj.FindNewMother ( r:longint;filehdr:fileheadertype);
- var rechdr:leaftype;
- Function FindMother(var direction:directiontype):longint;
- var Hdr :leaftype;
- LastNode:longint;
- procedure Search_Tree(n:longint);
- begin
- ReadRecLeaf(n,Hdr);
- if RecHdr.Key>Hdr.Key then
- if not(Hdr.Right=0) then Search_Tree(Hdr.Right) else
- begin
- LastNode:=n;
- Direction:=Right;
- end
- else if RecHdr.Key<Hdr.Key then
- if not(Hdr.Left=0) then Search_Tree(Hdr.Left) else
- begin
- LastNode:=n;
- Direction:=left;
- end;
- end;
- begin
- Search_Tree(filehdr.root);
- FindMother:=LastNode;
- end;
-
- var mhdr:leaftype;
- mrec:longint;
- motherdirection:directiontype;
- begin
- ReadRecLeaf(r,RecHdr);
- mrec:=FindMother(motherdirection);
- ReadRecLeaf(mrec,MHdr);
- RecHdr.Mother := mrec;
- Case MotherDirection of
- Right: MHdr.Right:=r;
- Left : MHdr.Left :=r;
- end;
- WriteRecLeaf(mrec,MHdr);
- WriteRecLeaf(r,RecHdr);
- end;
-
- Function bTreeObj.FindKeyRec (key: keytype):longint;
- var filehdr:fileheadertype;
- rechdr :leaftype;
- procedure FindKey(r:longint);
- begin
- ReadRecLeaf(r,RecHdr);
- if Key>RecHdr.Key then
- if not(RecHdr.Right=0) then FindKey(RecHdr.Right) else
- begin
- FindKeyRec:=0;
- end
- else if Key<RecHdr.Key then
- if not(RecHdr.Left=0) then FindKey(RecHdr.Left) else
- begin
- FindKeyRec:=0;
- end
- else if Key=RecHdr.Key then FindKeyRec:=r;
- end;
- begin
- ReadFileHdr(filehdr);
- if filehdr.root=0 then FindKeyRec:=0 else FindKey(filehdr.root)
- end;
-
- end.